package com.s7175.kor.gui.datahandling;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;

import com.s7175.kor.data.DB40Data;
import com.s7175.kor.model.ArchitectureView;
import com.s7175.kor.model.Attribute;
import com.s7175.kor.model.Component;
import com.s7175.kor.model.Configuration;
import com.s7175.kor.model.Parameter;
import com.s7175.kor.model.Server;

public class DataHandler extends JFrame {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private final JPanel contentPane;
	private final JTree tree;
	private JMenuItem mntmNewMenuItem;

	private final DB40Data data;
	private final DatabankBridge dbHandler;

	private ArchitectureView currentArch;

	JPopupMenu Pmenu;

	private static final int MENU_COMPONENT_ADD = 0;
	private static final int MENU_COMPONENT_REMOVE = 1;

	private static final int MENU_SERVER_ADD = 2;
	private static final int MENU_SERVER_REMOVE = 3;

	private static final int MENU_CONFIGURATION_ADD = 4;
	private static final int MENU_CONFIGURATION_REMOVE = 5;

	private static final int MENU_PARAMETER_ADD = 6;
	private static final int MENU_PARAMETER_REMOVE = 7;

	private static final int MENU_ATTRIBUTE_ADD = 8;
	private static final int MENU_ATTRIBUTE_REMOVE = 9;

	JMenuItem[] contextMenu = new JMenuItem[10];

	String[] contextMenuNames = { "Add component", "Remove component",
			"Add server", "Remove server", "Add Configuration",
			"Remove Configuration", "Add parameter", "Remove parameter",
			"Add attribute", "Remove attribute" };

	protected TreePath m_clickedPath;

	/**
	 * Create the frame.
	 */
	public DataHandler(DB40Data data, DatabankBridge bridge) {
		super();
		setTitle("Data Handler");
		setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		setBounds(100, 100, 800, 400);

		this.data = data;
		this.dbHandler = bridge;

		/**
		 * Menu items
		 */
		mntmNewMenuItem = new JMenuItem("Expand All");
		JMenuBar menuBar = new JMenuBar();
		setJMenuBar(menuBar);

		JMenu mnFile = new JMenu("File");
		menuBar.add(mnFile);

		JMenuItem mntmSave = new JMenuItem("Save");
		mntmSave.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				System.out.println("save clicked..");
				dbHandler.save();
			}
		});
		mnFile.add(mntmSave);

		JMenu mnArchitecture = new JMenu("Architecture");
		menuBar.add(mnArchitecture);
		
		mntmNewMenuItem.setEnabled(false);
		mntmNewMenuItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				for (int i = 0; i < tree.getRowCount(); i++) {
				    tree.expandRow(i);
				}
			}
		});
		mnArchitecture.add(mntmNewMenuItem);
		
				JMenuItem mntmNew = new JMenuItem("New...");
				mntmNew.setEnabled(false);
				mnArchitecture.add(mntmNew);

		JSeparator separator = new JSeparator();
		mnArchitecture.add(separator);

		/**
		 * Create popup menu for right click
		 */
		Pmenu = new JPopupMenu();

		// init menu items
		for (int i = 0; i < contextMenu.length; ++i) {
			contextMenu[i] = new JMenuItem(contextMenuNames[i]);
		}

		// add them
		_setMenu(MENU_COMPONENT_ADD, MENU_COMPONENT_REMOVE, MENU_SERVER_ADD,
				MENU_SERVER_REMOVE, MENU_CONFIGURATION_ADD,
				MENU_CONFIGURATION_REMOVE, MENU_ATTRIBUTE_ADD,
				MENU_ATTRIBUTE_REMOVE, MENU_PARAMETER_ADD,
				MENU_PARAMETER_REMOVE);

		contextMenu[MENU_CONFIGURATION_ADD]
				.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent arg0) {

						DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) tree
								.getLastSelectedPathComponent();
						// add to db with relations
						Server serv = (Server) selectedNode.getUserObject();

						if (serv.getConfItem() != null) {
							String err = "Please remove old configuration before setting new one.";
							JOptionPane.showMessageDialog(new JFrame(), err,
									"Application error",
									JOptionPane.ERROR_MESSAGE);
						} else {

							JTextField name = new JTextField();

							JTextField cores = new JTextField();
							JTextField debug = new JTextField();
							JTextField users = new JTextField();
							Object[] message = { "Name:", name, "CORES: ",
									cores, "USERS", users, "DEBUG?", debug };

							int option = JOptionPane.showConfirmDialog(null,
									message, "Component details",
									JOptionPane.OK_CANCEL_OPTION);
							if (option == JOptionPane.OK_OPTION) {
								String n = name.getText();
								String c = cores.getText();
								String u = users.getText();
								String d = debug.getText();

								if (!n.isEmpty() && !c.isEmpty()
										&& !u.isEmpty() && !d.isEmpty()) {

									Configuration conf = new Configuration(n);

									conf.setCores(Integer.valueOf(c));
									conf.setDebug(Boolean.valueOf(d));
									conf.setUsers(Integer.valueOf(u));

									serv.setConfItem(conf);
									conf.setWorksOn(serv);

									dbHandler.add(conf);

									// add to tree view
									DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(
											conf.getName());
									selectedNode.insert(childNode,
											selectedNode.getChildCount());
									childNode.setUserObject(conf);
									childNode.setAllowsChildren(true);

									// tree.getModel().validate();
									((DefaultTreeModel) (tree.getModel()))
											.reload();

									tree.expandPath(new TreePath(selectedNode
											.getPath()));
								}
							}
						}
					}
				});

		contextMenu[MENU_CONFIGURATION_REMOVE]
				.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent arg0) {
						DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) tree
								.getLastSelectedPathComponent();

						Configuration conf = (Configuration) selectedNode
								.getUserObject();

						_removeConfiguration(conf);
						dbHandler.remove(conf);

						((DefaultTreeModel) tree.getModel())
								.removeNodeFromParent(selectedNode);

					}

				});

		contextMenu[MENU_ATTRIBUTE_ADD].addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent arg0) {
				JTextField name = new JTextField();
				JTextField value = new JTextField();
				Object[] message = { "Name:", name, "Value:", value };

				int option = JOptionPane.showConfirmDialog(null, message,
						"Attribute details", JOptionPane.OK_CANCEL_OPTION);
				if (option == JOptionPane.OK_OPTION) {
					String n = name.getText();
					String v = value.getText();

					if (!n.isEmpty() && !v.isEmpty()) {
						DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) tree
								.getLastSelectedPathComponent();
						// add to db with relations
						Component comp = (Component) selectedNode
								.getUserObject();
						Attribute attr = new Attribute(n, v);

						comp.addAttribute(attr);
						attr.setComponent(comp);

						dbHandler.add(attr);

						// add to tree view
						DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(
								attr.getName());
						selectedNode.insert(childNode,
								selectedNode.getChildCount());
						childNode.setUserObject(attr);
						childNode.setAllowsChildren(false);

						// tree.getModel().validate();
						((DefaultTreeModel) (tree.getModel())).reload();

						tree.expandPath(new TreePath(selectedNode.getPath()));

					}
				}
			}

		});

		contextMenu[MENU_ATTRIBUTE_REMOVE]
				.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent arg0) {
						DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) tree
								.getLastSelectedPathComponent();

						Attribute attr = (Attribute) selectedNode
								.getUserObject();
						_removeAttribute(attr);

						((DefaultTreeModel) tree.getModel())
								.removeNodeFromParent(selectedNode);

					}

				});

		contextMenu[MENU_PARAMETER_ADD].addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				JTextField name = new JTextField();
				JTextField value = new JTextField();
				Object[] message = { "Name:", name, "Value:", value };

				int option = JOptionPane.showConfirmDialog(null, message,
						"Parameter details", JOptionPane.OK_CANCEL_OPTION);
				if (option == JOptionPane.OK_OPTION) {
					String n = name.getText();
					String v = value.getText();

					if (!n.isEmpty() && !v.isEmpty()) {
						DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) tree
								.getLastSelectedPathComponent();
						// add to db with relations
						Configuration conf = (Configuration) selectedNode
								.getUserObject();
						Parameter p = new Parameter(n);
						p.setValue(v);
						conf.addParameter(p);
						dbHandler.add(p);

						// add to tree view
						DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(
								p.getName());
						selectedNode.insert(childNode,
								selectedNode.getChildCount());
						childNode.setUserObject(p);
						childNode.setAllowsChildren(false);

						// tree.getModel().validate();
						((DefaultTreeModel) (tree.getModel())).reload();

						tree.expandPath(new TreePath(selectedNode.getPath()));

					}
				}
			}

		});

		contextMenu[MENU_PARAMETER_REMOVE]
				.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent arg0) {
						DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) tree
								.getLastSelectedPathComponent();

						Parameter par = (Parameter) selectedNode
								.getUserObject();
						_removeParameter(par);
						dbHandler.remove(par);

						((DefaultTreeModel) tree.getModel())
								.removeNodeFromParent(selectedNode);
					}

				});

		contextMenu[MENU_SERVER_REMOVE].addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent arg0) {

				DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) tree
						.getLastSelectedPathComponent();
				Server server = (Server) selectedNode.getUserObject();

				if (_removeServer(server)) {
					// tree.getModel().validate();
					((DefaultTreeModel) tree.getModel())
							.removeNodeFromParent(selectedNode);
				}
			}
		});

		contextMenu[MENU_SERVER_ADD].addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				JTextField servername = new JTextField();
				JTextField domain = new JTextField();
				Object[] message = { "Hostname:", servername, "Domain:", domain };

				int option = JOptionPane.showConfirmDialog(null, message,
						"Server Details", JOptionPane.OK_CANCEL_OPTION);
				if (option == JOptionPane.OK_OPTION) {
					String host = servername.getText();
					String dom = domain.getText();
					int myid = DB40Data.sequenceServerID();
					// proceed to add

					DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) tree
							.getLastSelectedPathComponent();
					Component parentComponent = (Component) selectedNode
							.getUserObject();
					Server server = new Server(host, dom, myid);
					parentComponent.addServer(server);
					dbHandler.add(server);

					// add to tree view
					DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(
							host);
					selectedNode.insert(childNode, selectedNode.getChildCount());
					childNode.setUserObject(server);
					childNode.setAllowsChildren(true);

					// tree.getModel().validate();
					((DefaultTreeModel) (tree.getModel())).reload();

					tree.expandPath(new TreePath(selectedNode.getPath()));
				}
			}
		});

		contextMenu[MENU_COMPONENT_ADD].addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				// System.out.println("add " + e);
				String test1 = JOptionPane
						.showInputDialog("Please enter new component name: ");

				if (test1 != null && !test1.isEmpty()) {
					DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) tree
							.getLastSelectedPathComponent();

					Component parentComponent = (Component) selectedNode
							.getUserObject();
					Component newChild = new Component(test1);
					newChild.setParent(parentComponent);
					parentComponent.addChild(newChild);

					dbHandler.add(newChild);

					DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(
							test1);
					childNode.setUserObject(newChild);
					selectedNode.insert(childNode, selectedNode.getChildCount());

					// tree.getModel().validate();
					((DefaultTreeModel) (tree.getModel())).reload();

					tree.expandPath(new TreePath(selectedNode.getPath()));
				}
			}
		});

		contextMenu[MENU_COMPONENT_REMOVE]
				.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent e) {
						System.out.println("remove " + e);

						DefaultMutableTreeNode node;
						DefaultTreeModel model = (DefaultTreeModel) (tree
								.getModel());

						TreePath[] paths = tree.getSelectionPaths();
						for (int i = 0; i < paths.length; i++) {
							node = (DefaultMutableTreeNode) (paths[i]
									.getLastPathComponent());

							if (!node.isRoot()) {
								Component removedComponent = (Component) node
										.getUserObject();

								_removeComponent(removedComponent);
								model.removeNodeFromParent(node);
							}
						}

					}
				});

		for (final ArchitectureView arch : DB40Data.getData().getArchList()) {
			JMenuItem oMenuItem = new JMenuItem(arch.getName());
			oMenuItem.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					System.out.println("picked arch " + arch.getName());
					changeArchitecture(arch);
				}
			});
			mnArchitecture.add(oMenuItem);

		}

		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		contentPane.setLayout(new BorderLayout(0, 0));
		setContentPane(contentPane);

		tree = new JTree();
		tree.setRootVisible(false);
		tree.setShowsRootHandles(true);
		tree.setModel(new DefaultTreeModel(new DefaultMutableTreeNode("JTree") {

			/**
			 * 
			 */
			private static final long serialVersionUID = 1L;
		}));
		
		JScrollPane scrollTree = new JScrollPane(tree,
				JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
				JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
		contentPane.add(scrollTree, BorderLayout.CENTER);

		MouseListener ml = new MouseAdapter() {
			@Override
			public void mousePressed(MouseEvent e) {
				if (SwingUtilities.isRightMouseButton(e)) {

					int row = tree.getClosestRowForLocation(e.getX(), e.getY());
					tree.setSelectionRow(row);

					DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) tree
							.getLastSelectedPathComponent();

					Object o = selectedNode.getUserObject();

					System.out.println("type?");
					System.out.println(o.getClass());

					if (o instanceof Component) {
						_setMenu(MENU_COMPONENT_ADD, MENU_COMPONENT_REMOVE,
								MENU_SERVER_ADD, MENU_ATTRIBUTE_ADD);
					} else if (o instanceof Server) {
						_setMenu(MENU_SERVER_REMOVE, MENU_CONFIGURATION_ADD);
					} else if (o instanceof Configuration) {
						_setMenu(MENU_CONFIGURATION_REMOVE, MENU_PARAMETER_ADD);
					} else if (o instanceof Parameter) {
						_setMenu(MENU_PARAMETER_REMOVE);
					} else if (o instanceof Attribute) {
						_setMenu(MENU_ATTRIBUTE_REMOVE);
					} else {
						_setMenu();
					}

					Pmenu.show(e.getComponent(), e.getX(), e.getY());
				}
			}
		};
		tree.addMouseListener(ml);

	}

	private void _setMenu(Integer... menus) {
		Pmenu.removeAll();
		if (menus != null) {
			for (int i = 0; i < menus.length; ++i) {
				Pmenu.add(contextMenu[menus[i]]);
			}
		}
	}

	protected void _removeAttribute(Attribute attr) {
		// remove relationship
		attr.setComponent(null);

		// remove from database
		dbHandler.remove(attr);
	}

	public void changeArchitecture(ArchitectureView newArch) {
		mntmNewMenuItem.setEnabled(true);
		currentArch = newArch;
		DefaultMutableTreeNode root = new DefaultMutableTreeNode(
				newArch.getName());
		DefaultTreeModel oModel = new DefaultTreeModel(root);

		for (Component c : newArch.oChildList) {
			walkTree(root, c);
		}

		tree.setModel(oModel);
		tree.validate();
	}

	private void walkTree(DefaultMutableTreeNode node, Component comp) {
		DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(
				comp.getName() + "#" + comp.toString());
		newNode.setUserObject(comp);
		node.add(newNode);
		for (Component c : comp.getChildList()) {
			walkTree(newNode, c);
		}
		for (Server s : comp.getServers()) {
			DefaultMutableTreeNode serverNode = new DefaultMutableTreeNode(
					s.getName());
			serverNode.setUserObject(s);
			newNode.add(serverNode);

			Configuration conf = s.getConfItem();
			DefaultMutableTreeNode confNode = new DefaultMutableTreeNode(
					conf.getName());
			confNode.setUserObject(conf);
			serverNode.add(confNode);

			for (Parameter p : conf.getParameters()) {
				DefaultMutableTreeNode pNode = new DefaultMutableTreeNode(
						p.getName());
				pNode.setUserObject(p);
				pNode.setAllowsChildren(false);
				confNode.add(pNode);
			}

		}
		for (Attribute a : comp.getAttributes()) {
			DefaultMutableTreeNode attributeNode = new DefaultMutableTreeNode(
					a.getName());
			attributeNode.setUserObject(a);
			attributeNode.setAllowsChildren(false);
			newNode.add(attributeNode);
		}
	}

	private boolean _removeServer(Server server) {
		server.setWorksIn(null);

		// cleanup step for stuff inside
		_removeConfiguration(server.getConfItem());

		// remove from database
		dbHandler.remove(server);

		return true;
	}

	private boolean _removeConfiguration(Configuration conf) {

		for (Parameter p : conf.getParameters()) {
			// cleanup step for stuff inside
			_removeParameter(p);
		}

		while (conf.getParameters().size() > 0) {
			conf.getParameters().remove();
		}

		// remove from database
		dbHandler.remove(conf);

		return true;
	}

	private boolean _removeParameter(Parameter p) {
		// remove linking to Configuration
		p.setBelongTo(null);

		// remove from database
		dbHandler.remove(p);

		return true;
	}

	private boolean _removeComponent(Component oToBeRemoved) {
		if (oToBeRemoved.getParent() == null) {
			// remove from current linking (architecture)
			currentArch.getComponents().remove(oToBeRemoved);
		}

		for (Server s : oToBeRemoved.getServers()) {
			_removeServer(s);
		}

		oToBeRemoved.getServers().clear();

		for (Attribute a : oToBeRemoved.getAttributes()) {
			_removeAttribute(a);
		}

		oToBeRemoved.getAttributes().clear();

		// process also for children
		for (Component c : oToBeRemoved.getComponents()) {
			if (!_removeComponent(c)) {
				// some issue but we will not try to be superhero
				// and bring back the rest (no rollback!)
				System.out.println("Some error while removing!");
			}
		}
		oToBeRemoved.getComponents().clear();

		// remove from database
		dbHandler.remove(oToBeRemoved);

		return true;

	}

	public JTree getTree() {
		return tree;
	}

	public DB40Data getData() {
		return data;
	}
}
